function a() {
  m = 3
}
a()
function b() {
  console.log(m) // 3
}
b();
(function () {
  // ƒ sum () {
    // return 10
  // }
  console.log(sum) // 函数声明方式的函数会替身到当前作用域的最前面,按顺序提升。当访问的时候，因为是按顺序提升的，自然而然的会出现后者覆盖前者

  // 1.想向先将函数声明按“块”提升至作用域最前

  // 2.然后在将变量由下向上访问，看能访问到哪一个
  
  // ƒ sum (num1, num2) {
  // return num1 + num2
  // }
  function sum(num1, num2) {
    return num1 + num2
  }
  function sum () {
    return 10
  }
  var sum = function (num1, num2) {
    return num1 + num2
  }
}());

// 函数名即对象指针
(function () {
  function sum (num1, num2) {
    return num1 + num2
  }
  console.log(sum(10, 10)) // 20

  var anotherSum = sum
  console.log(anotherSum(10, 10)) // 20
  sum = null //让这个指针指向了null
  console.log(anotherSum(10, 10)) // 20
}());

// 没有重载
(function () {
  //  ƒ addSomeNumber (num) {
    // return num + 200
  // }
  console.log(addSomeNumber) 
  function addSomeNumber (num) { // 读取函数声明，被提到了最顶部
    return num + 100
  }
  function addSomeNumber (num) { // 当读取到他时，被提升到了 num + 100 的下面
    return num + 200
  }
  console.log(addSomeNumber(100)) // 300
  var addSomeNumber = function (num) {
    return num + 300
  }
  //  ƒ (num) {
    // return num + 300
  // }
  console.log(addSomeNumber) // 因为到这里，又重新赋值了
  addSomeNumber = function (num) {
    return num + 400
  }
  console.log(addSomeNumber(100)) // 500
}());

// 函数声明与函数表达式
(function () {
  // ƒ sum (num1, num2) {
    // return num1 + num2
  // }
  console.log(sum)
  console.log(sum(3, 3))  // 6
  var a = 3
  var sum = 1
  console.log(sum) // 1
  function sum (num1, num2) {
    return num1 + num2
  }
  var sum = function (num1, num2) {
    return num1 * num2
  }
  console.log(sum(3, 3)) // 9
}());
// 作为值的函数
(function () {
  function callSomeFunction (someFuction, someArgument) {
    return someFuction(someArgument)
  }
  function add10 (num) {
    return num + 10
  }
  var result1 = callSomeFunction(add10, 10)
  console.log(result1) // 20
  function greeting (name) {
    return "Hello " + name
  }
  var result2 = callSomeFunction(greeting, 'monkey')
  console.log(result2) // Hello monkey

  // 从一个函数中返回另一个函数
  function creatCom (propertyName) {
    return function (object1, object2) {
      var value1 = object1[propertyName]
      var value2 = object2[propertyName]
      if (value1 < value2) {
        return -1
      } else if (value1 > value2) {
        return 1
      } else {
        return 0
      }
      // 比较函数中为什么不能直接写数字相减的方式 比如，比较函数最后计算的值为NaN
//       'n' > 'z'
//        false
//       'n' - 'z'
//        NaN
//        Number(NaN)
//        NaN
    }
  }
  var data = [{name: 'Zachary', age: 28}, {name: 'Nicholas', age: 29}]
  console.log(data.concat().sort(creatCom('name')))
  console.log(data.concat().sort(creatCom('age')))
}());

// 函数内部属性
(function () {
  // arguments.callee 指向拥有arguments对象的函数
  function factorial (num) {
    if (num <= 1) {
      return 1
    } else {
      return num * factorial(num - 1)
    }
  }
  console.log(factorial(4)) // 24

  function factorial2 (num) {
    if (num <= 1) {
      return 1
    } else {
      return num * arguments.callee(num - 1)
    }
  }
  console.log(factorial2(4)) // 24
  // 解耦之后的优点
  var trueFactoril = factorial2
  factorial2 = function () {
    return 0
  }
  console.log(trueFactoril(5)) // 120
  console.log(factorial2(5)) // 0
}());
// 函数中的 this 指向当前执行环境
(function (){
  window.color = 'red'
  var o = { color: 'blue' }
  function satColor () {
    console.log(this.color)
  }
  satColor() // red
  o.satColor = satColor
  o.satColor() // blue

  // 函数中的 caller 指向调用此函数的函数
  function outer () {
    inner()
  }
  function inner () {
    console.log(inner.caller)
  }
  // ƒ outer () {
    // inner()
  // }
  outer()

}());

// caller 也可以用 arguments.callee.caller 来代替以解决耦合性
(function (){
  function outer () {
    inner()
  }
  function inner () {
    console.log(arguments.callee.caller)
  }
  // ƒ outer () {
    // inner()
  // }
  outer()
}());
// 函数的 call/apply 方法
(function (){
   function sum (num1, num2) {
     return num1 + num2
   }
   function callSum1 (num1, num2) {
     return sum.apply(this, arguments)
   }
   function callSum2 (num1, num2) {
     return sum.apply(this, [num1, num2])
   }
   console.log(callSum1(5, 5)) // 10
   console.log(callSum2(5, 5)) // 10

   function callSum (a, b) {
    //  将sum的this指向callSum的this
     return sum.call(this, a, b)
   }
   console.log(callSum(5, 5)) // 10
   console.log(callSum(5)) // NaN 5 + undefined = NaN undefined 转化数字为 NaN,
   // null 转化为数字是 0
}());

// call、apply 改变函数的执行环境
(function () {
  window.color = 'red'
  var o = { color: 'blue'}
  function sayColor () {
    console.log(this.color)
  }
  sayColor.call(this) // red
  sayColor.call(window) // red
  // 将sayColor的This指向o的this
  sayColor.call(o) //blue

  var f = sayColor.bind(o)
  f() // blue
}())

